/*
	proc_setup.c

	contains the setup process's functionality

	Author: Aidan Goddard 25/7/13
*/

#include"../driver/general/driver_error_codes.h"

#include"../setup/headers/printf.h"
#include"../klib/headers/klib_proc_threads.h"
#include"../klib/headers/klib_message_passing.h"
#include"../klib/headers/klib_IRQ_control.h"
#include"../klib/headers/klib_keyboard_buffer.h"
#include"headers/process_control.h"
#include"headers/scheduler_functions.h"
#include"../driver/AHCI/ahci.h"

#include"headers/system_error.h"

/*
	builtin driver setup routines
*/
extern uint32_t PS2_KB_INIT(uint32_t *error);
extern uint32_t HPET_INIT(uint32_t *error);
extern uint32_t AP_INIT(uint32_t *error);
extern uint32_t ACDM_INIT(uint32_t *error);

// process entry points
extern uint64_t proc_proc_manager(void);
extern uint64_t proc_system_CPU_monitor(void);
extern uint64_t PS2_KB_main(void);
extern uint64_t ACDM_main(void);
extern uint64_t ProcStartWindow(void);
extern uint64_t ProcResourceWindow(void);
extern uint64_t ProcProcsWindow(void);
extern uint64_t ProcWindowsWindow(void);
extern uint64_t ProcSystemError(void);
extern uint64_t ProcTime(void);
extern uint64_t AHCI_main(void);


static HWND hwnd;
static int direction = 1;
char *buff;

// test thread
static uint64_t test_thread(void *data)
{
	// start coordinates
	int x = 40;
	int y = 11;

	// set the buffer defaults (0x1f)
	int i;
	for(i = 0; i < 3680; i += 2)
	{
		buff[i] = 0;
		buff[i + 1] = 0x1f;
	}

	// main loop
	while(1)
	{
		// wait
		__KLIB__SleepThisThread(500000);

		// blank current position
		buff[((y * 80) + x) * 2] = 0;
		buff[((y * 80) + x) * 2 + 1] = 0x1f;

		// check input direction
		if(direction == 2)	// down
		{
			// check limits
			y++;
			if(y >= 23)
			{
				y = 0;
			}
		}
		else if(direction == 1) // up
		{
			y--;
			if(y < 0)
			{
				y = 22;
			}
		}
		else if(direction == 3) // left
		{
			x--;
			if(x < 0)
			{
				x = 79;
			}
		}
		else if(direction == 4) // right
		{
			x++;
			if(x >= 80)
			{
				x = 0;
			}
		}
		else
		{
		}

		// draw new position
		buff[((y * 80) + x) * 2] = 219;
		buff[((y * 80) + x) * 2 + 1] = 0x1f;

		// update the window
		ACDMWindowUpdate(hwnd);

	}



	// finished
	__KLIB__KillThisThread(0);
	return 0;
}



/*
	process entry point
*/

void proc_system_setup(void)
{
	// if we're here, then the scheduler successfully started
	printf(" done");

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise the HPET Driver
	printf("\n[STARTUP] Initialising system HPET...");
	uint32_t HPET_error_2 = 0;
	uint32_t HPET_error_1 = HPET_INIT(&HPET_error_2);
	if(HPET_error_1 != DRIVER_STARTUP_SUCCESS)
	{
		printf(" ERROR: 0x%x 0x%x", HPET_error_1, HPET_error_2);
		__KLIB__WaitThisThread();
	}

	// have a brief sleep to test it
	// if the system hangs here, then something went wrong with the HPET init routine
	__KLIB__SleepThisThread(10000);
	//__KLIB__WaitThisThread();
	printf(" done");

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise APs
	printf("\n[STARTUP] Initialising system Application Processors...");
	uint32_t AP_error_2 = 0;
	uint32_t AP_error_1 = AP_INIT(&AP_error_2);
	if(AP_error_1 != DRIVER_STARTUP_SUCCESS)
	{
		printf(" ERROR: 0x%x 0x%x", AP_error_1, AP_error_2);
		__KLIB__WaitThisThread();
	}
	printf(" done");

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise Boot Disk Driver(s)
	printf("\n[STARTUP] Initialising boot storage drivers...");
	__KLIB__SleepThisThread(100000);
	uint32_t storage_error = AHCI_INIT();
	if(storage_error == AHCI_INIT_SUCCESS)
	{
		printf(" done\n[STARTUP] Device on AHCI controller");
	}
	else
	{
		printf(" ERROR 0x%x\n[STARTUP] ERROR: Boot storage device not found", storage_error);
		__KLIB__WaitThisThread();
	}

	printf("\n[STARTUP] Initialising boot filesystem driver...");
	uint32_t filesystem_error = FAT32_INIT();
	if(filesystem_error == FAT32_SUCCESS)
	{
		printf(" done");
	}
	else
	{
		printf(" ERROR 0x%x\n[STARTUP] ERROR boot storage filesystem not supported or corrupt", filesystem_error);
		__KLIB__WaitThisThread();
	}

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise cleanup and monitor processes
	printf("\n[STARTUP] Initialising process manager...");
	if(__KLIB__SpawnSystemProcess(proc_proc_manager, (void*)(KOFFSET + 0x3c00000), 0, 0, "system_process_manager.sys", 27, "The system process manager", 27) == 0)
	{
		printf(" ERROR: could not initialise process manager");
		__KLIB__WaitThisThread();
	}
	printf(" done");

	printf("\n[STARTUP] Initialising system monitor process...");
	if(__KLIB__SpawnSystemProcess(proc_system_CPU_monitor, (void*)(KOFFSET + 0x3b00000), 0, 0, "system_monitor.sys", 19, "The system montior process", 27) == 0)
	{
		printf(" ERROR: could not initialise system monitor process");
		__KLIB__WaitThisThread();
	}
	printf(" done");

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise PS/2 driver process
	printf("\n[STARTUP] Initialising PS/2 driver...");
	if(__KLIB__SpawnSystemProcess(PS2_KB_main, (void*)(KOFFSET + 0x3a00000), 0, 0, "PS2_keyboard.sys", 17, "The PS/2 keyboard driver process", 33) == 0)
	{
		printf(" ERROR: could not initialise PS/2 driver process");
		__KLIB__WaitThisThread();
	}
	printf(" done");

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise the ACDM processes
	// built-in window processes
	printf("\n[STARTUP] Initialising ACDM window control processes");
	if(__KLIB__SpawnSystemProcess(ProcStartWindow, (void*)(KOFFSET + 0x3800000), 0, 0, "ACDM_start.sys", 15, "The ACDM start window process", 30) == 0)
	{
		printf("\n[STARTUP] ERROR: could not initialise ACDM start window process");
		__KLIB__WaitThisThread();
	}
	printf(".");
	if(__KLIB__SpawnSystemProcess(ProcResourceWindow, (void*)(KOFFSET + 0x3700000), 0, 0, "ACDM_resource.sys", 18, "The ACDM resources window process", 34) == 0)
	{
		printf("\n[STARTUP] ERROR: could not initialise ACDM resources window process");
		__KLIB__WaitThisThread();
	}
	printf(".");
	if(__KLIB__SpawnSystemProcess(ProcProcsWindow, (void*)(KOFFSET + 0x3600000), 0, 0, "ACDM_procs.sys", 15, "The ACDM processes window process", 34) == 0)
	{
		printf("\n[STARTUP] ERROR: could not initialise ACDM processes window process");
		__KLIB__WaitThisThread();
	}
	printf(".");
	if(__KLIB__SpawnSystemProcess(ProcWindowsWindow, (void*)(KOFFSET + 0x3500000), 0, 0, "ACDM_windows.sys", 17, "The ACDM windows window process", 32) == 0)
	{
		printf("\n[STARTUP] ERROR: could not initialise ACDM windows window process");
		__KLIB__WaitThisThread();
	}
	printf(".");
	if(__KLIB__SpawnSystemProcess(ProcSystemError, (void*)(KOFFSET + 0x3400000), 0, 0, "ACDM_error.sys", 15, "The ACDM error window process", 30) == 0)
	{
		printf("\n[STARTUP] ERROR: could not initialise ACDM error window process");
		__KLIB__WaitThisThread();
	}
	printf(".");
	if(__KLIB__SpawnSystemProcess(ProcTime, (void*)(KOFFSET + 0x3300000), 0, 0, "ACDM_time.sys", 14, "The ACDM time control process", 30) == 0)
	{
		printf("\n[STARTUP] ERROR: could not initialise ACDM time control process");
		__KLIB__WaitThisThread();
	}
	printf(". done");


	// main process
	printf("\n[STARTUP] Initialising ACDM main process...");
	if(__KLIB__SpawnSystemProcess(ACDM_main, (void*)(KOFFSET + 0x3900000), 0, 0, "ACDM.sys", 9, "The ACDM main process", 22) == 0)
	{
		printf(" ERROR: could not initialise ACDM main process");
		__KLIB__WaitThisThread();
	}
	printf(" done");


	////////////////////////////////////////////////////////////////////////////////////////
	// initialise the AHCI demo window
	//printf("\n[STARTUP] Initialising AHCI driver...");
	if(__KLIB__SpawnSystemProcess(AHCI_main, (void*)(KOFFSET + 0x3200000), 0, 0, "AHCI.sys", 9, "The ACHI driver process", 24) == 0)
	{
		//printf(" ERROR: could not initialise AHCI driver process");
		ACDMPrintLog("ERROR: could not initialise AHCI driver process", 29);
		__KLIB__WaitThisThread();
	}
	//printf(" done");


	// wait 5 seconds
	__KLIB__SleepThisThread(1000000);


	// multithreading demonstration stuff
	// create a window
	hwnd = ACDMCreateStaticWindow(__KLIB__RCGetPID(), "multi-threading demo 1", 22);
	ACDMWindowShow(hwnd);
	buff = (char*)ACDMWindowGetBufferAddress(hwnd);

	// start the UI thread
	__KLIB__SpawnNewThread(test_thread, NULL, (void*)(KOFFSET + 0x3c80000));

	// wait on user input
	while(hwnd)
	{
		uint8_t input = ACDMWindowWaitForChar(hwnd);

		// up key
		if(input == 193)
		{
			direction = 1;
		}
		// down
		else if(input == 194)
		{
			direction = 2;
		}
		// left
		else if(input == 180)
		{
			direction = 3;
		}
		// right
		else if(input == 195)
		{
			direction = 4;
		}
		// other
		else
		{
		}


	}




/*

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise PS/2 driver process
	printf("\n[STARTUP] Initialising PS/2 driver...");
	uint32_t PS2_error_2 = 0;
	uint32_t PS2_error_1 = PS2_KB_INIT(&PS2_error_2);
	if(PS2_error_1 != DRIVER_STARTUP_SUCCESS)
	{
		printf(" ERROR: 0x%x 0x%x", PS2_error_1, PS2_error_2);
		__KLIB__WaitThisThread();
	}
	printf(" done");

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise the VFS

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise the PCI Driver

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise the IDE driver

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise the FAT32 driver

	////////////////////////////////////////////////////////////////////////////////////////
	// initialise the ACDM
	printf("\n[STARTUP] Initialising ACDM...");

	uint32_t ACDM_error_2 = 0;
	uint32_t ACDM_error_1 = ACDM_INIT(&ACDM_error_2);
	if(ACDM_error_1 != DRIVER_STARTUP_SUCCESS)
	{
		printf(" ERROR: 0x%x 0x%x", ACDM_error_1, ACDM_error_2);
		__KLIB__WaitThisThread();
	}


	*/

	// finished initiating drivers and the display manager has started. can now stop this process
	KillThisProcess(0);
	//__KLIB__WaitThisThread();
}
